/**
 * Copyright (c) 2008, Keisuke Inoue, Syracuse Unviersity
 * All rights reserved.
 *
 */
package edu.syr.iis.web.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;

import javax.persistence.Transient;

import edu.syr.iis.web.util.HibernateUtil;

import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Session;

/**
 * Manages database operations for Conversation table
 * @author kinoue
 *
 */
public class ConversationManager
{
	
	public double getAverageWindowDiff(User u1, User u2)
	{
		Collection<Conversation> conversations = getConversations(u1, u2);
		double sumWindowDiffs = 0.0;
		
		for (Conversation conversation: conversations)
			sumWindowDiffs += conversation.getWindowDiff(u1, u2);
		
		return sumWindowDiffs / conversations.size();			
	}
	
	public double getAverageAverageWindowDiff(User u1, User u2)
	{
		Collection<Conversation> conversations = getConversations(u1, u2);
		double sumWindowDiffs = 0.0;
		
		for (Conversation conversation: conversations)
			sumWindowDiffs += conversation.getAverageWindowDiff(u1, u2);
		
		return sumWindowDiffs / conversations.size();			
	}
		
	
	/**
	 * Returns list of all conversations in a specified dataset
	 *  ordered by conversationId
	 */


	/**
	 * Returns list of all Conversation database records sorted by conversationId
	 */
	public List<Conversation> getConversations(Dataset dataset)
	{
		return getConversations(dataset, true);
	}

	public List<Conversation> getConversations(Dataset dataset, boolean lazy)
	{
		List<Conversation> list = null;
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		try
		{				
			list  = session.createQuery("FROM Conversation " + 
			" WHERE datasetId = ?").setInteger(0, dataset.datasetId).list();

			greedyFetch(list);

			session.getTransaction().commit();
		}
		catch (HibernateException e)
		{
			session.getTransaction().rollback();
			throw e;
		}
		return list;
	}



	/**
	 * Returns list of all Conversation database records sorted by conversationId
	 */
	public List<Conversation> getConversations(int datasetId)
	{
		return getConversations(datasetId, true);
	}

	public List<Conversation> getConversations(int datasetId, boolean lazy)
	{
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();
		Dataset dataset = (Dataset) session.get(Dataset.class, datasetId);
		session.getTransaction().commit();

		return getConversations(dataset, lazy);
	}

	/**
	 * Returns list of all Conversation database records sorted by daasetId
	 */
	public List<Conversation> getConversations()
	{
		List<Conversation> list = null;
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		try
		{
			list = session.createQuery("FROM Conversation ORDER BY ConversationId ").list();
			greedyFetch(list);
			session.getTransaction().commit();
		}
		catch (HibernateException e)
		{
			session.getTransaction().rollback();
			throw e;
		}
		return list;
	}

	/**
	 * Returns list of all Conversation database records sorted by conversationId
	 */
	public List<Conversation> getConversations(Dataset dataset, ConversationLabel conversationLabel)
	{
		List<Conversation> list = null;
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		try
		{
			list = (List<Conversation>)
			session.createQuery("FROM Conversation WHERE " +
					" dataset = :dataset " +
			" AND conversationLabel = :conversationLabel")
			.setEntity("dataset", dataset)
			.setEntity("conversationLabel", conversationLabel);

			greedyFetch(list);
			session.getTransaction().commit();
		}
		catch (HibernateException e)
		{
			session.getTransaction().rollback();
			throw e;
		}
		return list;
	}

	/**
	 * Returns list of all Conversation database records sorted by conversationId
	 */
	public List<Conversation> getConversations(int datasetId, int conversationLabelId)
	{
		List<Conversation> list = null;
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		try
		{
			list = session.createQuery("FROM Conversation WHERE " +
					" datasetId = :datasetId " +
			" AND conversationLabelId = :conversationLabelId")
			.setInteger("datasetId", datasetId)
			.setInteger("conversationLabelId", conversationLabelId)
			.list();
			greedyFetch(list);
			session.getTransaction().commit();
		}
		catch (HibernateException e)
		{
			session.getTransaction().rollback();
			throw e;
		}
		return list;
	}

	
	@Transient
	public List<DisagreementMatrix> getIndividualDisagreementMatrices(User u1, User u2)
	{
		List<DisagreementMatrix> matrices = new ArrayList<DisagreementMatrix>();

		for (Conversation conversation: getConversations(u1, u2))
		{
			matrices.add(conversation.getDisagreementMatrix(u1, u2, Conversation.AGREE_FUNCTION));
			matrices.add(conversation.getDisagreementMatrix(u1, u2, Conversation.AGREE_PART_DOMAIN));
			matrices.add(conversation.getDisagreementMatrix(u1, u2, Conversation.AGREE_FULL_DOMAIN));
			matrices.add(conversation.getDisagreementMatrix(u1, u2, Conversation.AGREE_FUNCTION_PART_DOMAIN));
			matrices.add(conversation.getDisagreementMatrix(u1, u2, Conversation.AGREE_FUNCTION_FULL_DOMAIN));
		}

		return matrices;				
	}

	@Transient
	public List<DisagreementMatrix> getCumulativeDisagreementMatrices(User u1, User u2)
	{
		List<DisagreementMatrix> matrices = new ArrayList<DisagreementMatrix>();

		matrices.add(getCumulativeDisagreementMatrix(u1, u2, Conversation.AGREE_FUNCTION));
		matrices.add(getCumulativeDisagreementMatrix(u1, u2, Conversation.AGREE_PART_DOMAIN));
		matrices.add(getCumulativeDisagreementMatrix(u1, u2, Conversation.AGREE_FULL_DOMAIN));
		
		matrices.add(getCumulativeDisagreementMatrix(u1, u2, Conversation.AGREE_FUNCTION_PART_DOMAIN));				
		matrices.add(getCumulativeDisagreementMatrix(u1, u2, Conversation.AGREE_FUNCTION_FULL_DOMAIN));
		
		return matrices;				
	}

	@Transient
	public List<DisagreementMatrix> getCumulativeDisagreementMatrices(User u1, User u2, int level)
	{
		List<DisagreementMatrix> matrices = new ArrayList<DisagreementMatrix>();

		if ((level & Conversation.AGREE_FUNCTION) != 0)
		    matrices.add(getCumulativeDisagreementMatrix(u1, u2, Conversation.AGREE_FUNCTION));
		
		if ((level & Conversation.AGREE_PART_DOMAIN) != 0)
		    matrices.add(getCumulativeDisagreementMatrix(u1, u2, Conversation.AGREE_PART_DOMAIN));
		
		if ((level & Conversation.AGREE_FULL_DOMAIN) != 0)
		    matrices.add(getCumulativeDisagreementMatrix(u1, u2, Conversation.AGREE_FULL_DOMAIN));
		
		if ((level & Conversation.AGREE_FUNCTION_PART_DOMAIN) != 0)
		    matrices.add(getCumulativeDisagreementMatrix(u1, u2, Conversation.AGREE_FUNCTION_PART_DOMAIN));
		
		if ((level & Conversation.AGREE_FUNCTION_FULL_DOMAIN) != 0)
		    matrices.add(getCumulativeDisagreementMatrix(u1, u2, Conversation.AGREE_FUNCTION_FULL_DOMAIN));

		return matrices;
	}
	
	@Transient
	public List<DisagreementMatrix> getDisagreementMatrices(User u1, User u2, int level)
	{
		List<DisagreementMatrix> matrices = new ArrayList<DisagreementMatrix>();

		for (Conversation conversation: getConversations(u1, u2))
			matrices.add(conversation.getDisagreementMatrix(u1, u2, level));

		return matrices;				
	}
	
	@Transient
	public DisagreementMatrix getCumulativeDisagreementMatrix(User u1, User u2, int level)
	{
		return new DisagreementMatrix(u1, u2, getConversations(u1,u2), level);
	}	

	
	public List<Conversation> getConversations(User u1)
	{
		List<Conversation> list = null;

		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		try
		{
			String query = "from Conversation conversation where " +
			" conversation in " +
			" (select assignment.conversation from Assignment assignment " + 
			" where assignment.user = :user1) ";

			list = session.createQuery(query).setEntity("user1", u1).list();

			greedyFetch(list); 
			session.getTransaction().commit();
		}
		catch (HibernateException e)
		{
			session.getTransaction().rollback();
			throw e;
		}
		return list;
	}
	
	
	public List<Conversation> getConversations(User u1, User u2)
	{
		List<Conversation> list = null;

		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		try
		{
			String query = "from Conversation conversation where " +
			" conversation in " +
			" (select assignment.conversation from Assignment assignment " + 
			" where assignment.user = :user1) " +
			" and " +
			" conversation in " +
			" (select assignment.conversation from Assignment assignment " + 
			" where assignment.user = :user2) ";

			list = session.createQuery(query).setEntity("user1", u1).setEntity("user2", u2).list();

			greedyFetch(list); 
			session.getTransaction().commit();
		}
		catch (HibernateException e)
		{
			session.getTransaction().rollback();
			throw e;
		}
		return list;
	}


	public Conversation getPreviousConversation(Conversation conversation)
	{
		Conversation previous = null;

		String queryString = 
			"FROM Conversation c1" +
			" WHERE c1.conversationId = " +
			" (SELECT max(conversationId) FROM Conversation c2 " +
			" where c2.dataset.datasetId = ? and c2.conversationId < ?)";

		previous = getConversation(queryString, conversation.getDataset().getDatasetId(), conversation.getConversationId());

		if (previous == null) {
			queryString = 
				"FROM Conversation c1" +
				" WHERE c1.conversationId = " +
				" (SELECT max(conversationId) FROM Conversation c2 " +
				" where c2.dataset.datasetId = ? and c2.conversationId >= ?)";

			previous = getConversation(queryString, conversation.getDataset().getDatasetId(), conversation.getConversationId());				    
		}

		return previous;

	}

	public Conversation getPreviousConversation(Dataset dataset, Conversation conversation)
	{
		Conversation previous;
		final int datasetId = dataset.getDatasetId();
		final int conversationId = conversation.getConversationId();

		String queryString = 
			"FROM Conversation c1" +
			" WHERE c1.conversationId = " +
			" (SELECT max(conversationId) FROM Conversation c2 " +
			" where c2.dataset.datasetId = ? and c2.conversationId < ?)";

		previous = getConversation(queryString, datasetId, conversationId);

		if (previous == null) {
			queryString = 
				"FROM Conversation c1" +
				" WHERE c1.conversationId = " +
				" (SELECT max(conversationId) FROM Conversation c2 " +
				" where c2.dataset.datasetId = ? and c2.conversationId >= ?)";

			previous = getConversation(queryString, datasetId, conversationId);				    
		}

		return previous;

	}

	public Conversation getNextConversation(
			Conversation conversation)
	{
		Conversation next;
		int datasetId = conversation.getDataset().getDatasetId();
		int conversationId = conversation.getConversationId();

		String queryString = 
			"FROM Conversation c1" +
			" WHERE c1.conversationId = " +
			" (SELECT min(conversationId) FROM Conversation c2 " +
			" where c2.dataset.datasetId = ? and c2.conversationId > ?)";

		next = getConversation(queryString, datasetId, conversationId);

		if (next == null) {
			queryString = 
				"FROM Conversation c1" +
				" WHERE c1.conversationId = " +
				" (SELECT min(conversationId) FROM Conversation c2 " +
				" where c2.dataset.datasetId = ? and c2.conversationId <= ?)";

			next = getConversation(queryString, datasetId, conversationId);				    
		}

		return next;
	}

	/*
	 * Returns the previous conversation with the same conversation label
	 */
	public Conversation getPreviousConversationByConversationLabel(	    
			Conversation conversation
	)
	{
		final int datasetId = conversation.getDataset().getDatasetId();
		final int conversationId = conversation.getConversationId();
		final int conversationLabelId = conversation.getConversationLabel().getConversationLabelId();

		Conversation previous;

		String queryString = "FROM Conversation c1" +
		" WHERE c1.conversationId = " +
		" (SELECT max(conversationId) FROM Conversation c2 " +
		" where c2.dataset.datasetId = ? and c2.conversationId < ? and conversationLabelId = ?)";
		previous = getConversation(queryString, datasetId, conversationId, conversationLabelId);

		// If there is no previous one, go back to the end
		//
		if (previous == null) {
			queryString =     "FROM Conversation c1" +
			" WHERE c1.conversationId = " +
			" (SELECT max(conversationId) FROM Conversation c2 " +
			" where c2.dataset.datasetId = ? and c2.conversationId >= ? and conversationLabelId = ?)";
			previous = getConversation(queryString, datasetId, conversationId, conversationLabelId);
		}
		return previous;
	}

	public Conversation getNextConversationByConversationLabel(	    
			Conversation conversation
	)      
	{
		final int datasetId = conversation.getDataset().getDatasetId();
		final int conversationId = conversation.getConversationId();
		final int conversationLabelId = conversation.getConversationLabel().getConversationLabelId();

		Conversation next = null;
		String queryString =     "FROM Conversation c1" +
		" WHERE c1.conversationId = " +
		" (SELECT min(conversationId) FROM Conversation c2 " +
		" where c2.dataset.datasetId = ? and c2.conversationId > ? and conversationLabelId = ?)";

		next = getConversation(queryString, datasetId, conversationId, conversationLabelId);

		// If there is no next one, go back to the beginning
		//
		if (next == null) {
			queryString =     "FROM Conversation c1" +
			" WHERE c1.conversationId = " +
			" (SELECT min(conversationId) FROM Conversation c2 " +
			" where c2.dataset.datasetId = ? and c2.conversationId <= ? and conversationLabelId = ?)";
			next = getConversation(queryString, datasetId, conversationId, conversationLabelId);
		}
		return next;
	}

	public Conversation getConversation(
			String queryString,
			int datasetId,
			int conversationId
	)      
	{
		Conversation conversation = null;
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		try
		{
			conversation = (Conversation) session.createQuery(queryString)
			.setInteger(0, datasetId)
			.setInteger(1, conversationId)
			.uniqueResult();
			session.getTransaction().commit();
		}
		catch (HibernateException e)
		{
			session.getTransaction().rollback();
			throw e;
		}
		return conversation;
	}


	public Conversation getConversation(
			String queryString,
			int datasetId,
			int conversationId,
			int conversationLabelId
	)      
	{
		Conversation conversation = null;
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		try
		{
			conversation = (Conversation) session.createQuery(queryString)
			.setInteger(0, datasetId)
			.setInteger(1, conversationId)
			.setInteger(2, conversationLabelId)
			.uniqueResult();
			session.getTransaction().commit();
		}
		catch (HibernateException e)
		{
			session.getTransaction().rollback();
			throw e;
		}
		return conversation;
	}


	public Conversation getConversation(int conversationId)
	{
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		Conversation conversation = (Conversation) session.get(Conversation.class, conversationId);

		// Forcing initializing the collections
		//
		conversation.getMessages().size();
		conversation.getAnnotators().size();

		return conversation; 
	}

	/**
	 * Saves a Conversation object.
	 */
	public void saveConversation(Conversation conversation)
	{
		Session session = HibernateUtil.getSessionFactory()
		.getCurrentSession();
		session.beginTransaction();
		try
		{
			session.saveOrUpdate(conversation);
			session.getTransaction().commit();
		}
		catch (HibernateException e)
		{
			session.getTransaction().rollback();
			throw e;
		}
	}

	/**
	 * Deletes Conversation record with matching conversationId 
	 */
	public void deleteConversation(int conversationId)
	{
		Session session = 
			HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();	
		try
		{
			session.delete(session.load(Conversation.class,new Integer(conversationId)));         	    			
			session.flush();
			session.getTransaction().commit();
		}
		catch (HibernateException e)
		{
			session.getTransaction().rollback();
			throw e;
		}
	}

	void greedyFetch(Collection<Conversation> conversations)	
	{
		for (Conversation conversation: conversations)
			greedyFetch(conversation);
	}

	void greedyFetch(Conversation conversation)
	{
		Hibernate.initialize(conversation.getMessages());
		Hibernate.initialize(conversation.getAnnotators());

		for (Message message: conversation.getMessages())
			Hibernate.initialize(message.dialogueActs);

		for (User annotator: conversation.getAnnotators())
			Hibernate.initialize(annotator);
	}





}
